#include <hwicodec_common.h>
#include <stdlib.h>
#include <string.h>
#ifdef DMALLOC
#include <imp/dmalloc.h>
#endif
static int b_wait_id(id_pos_t *p_id_pos, int index, int *id, id_sync_t *sync)
{
	int id_distance = p_id_pos->id_elem.id_prop.max_id - p_id_pos->id_elem.id_prop.min_id + 1;
	int pos_header = p_id_pos->cur_pos;
	int pos_tail = (p_id_pos->cur_pos + id_distance - 1) % p_id_pos->pos_distance;
	id_node_t *p_id_node = p_id_pos->id_elem.p_id_node;
	int wait_flag = 0;
	int i = 0;
	int req_pos = index % p_id_pos->pos_distance;
	int req_id = -1;
	*id = -1;
	int prev_id_m1 = (sync->prev_id + 1) % p_id_pos->pos_distance;

	if ((req_pos == 0) && (sync->b_sync) && (prev_id_m1 == p_id_pos->cur_pos)) {
		if (prev_id_m1 == pos_header || req_pos == pos_header) {
			p_id_pos->pos_distance = sync->gopsize;
			p_id_pos->cur_pos = pos_header = 0;
			pos_tail = (p_id_pos->cur_pos + id_distance - 1) % p_id_pos->pos_distance;
		} else {
			wait_flag = 1;
			goto point_wait_flag;
		}
	}

	//printf("line:%d, req_pos = %d, [%d-%d]\n", __LINE__, req_pos, pos_header, pos_tail);
	if (((pos_tail >= pos_header) && ((req_pos < pos_header) || (req_pos > pos_tail)))
		|| ((pos_tail < pos_header) && ((req_pos < pos_header) && (req_pos > pos_tail)))) {
			wait_flag = 1;
			goto point_wait_flag;
	}

	if (p_id_pos->id_elem.id_prop.b_seq) {
		if (((pos_tail < pos_header) && (req_pos < pos_tail))
				|| p_id_node[req_pos % id_distance].b_using
				|| p_id_node[req_pos % id_distance].b_used) {
			wait_flag = 1;
			goto point_wait_flag;
		}
		req_id = req_pos % id_distance;
	} else {
		for (i = 0; i < id_distance; i++) {
			if ((!p_id_node[i].b_using) && (!p_id_node[i].b_used)) {
				break;
			}
		}
		if (i == id_distance) {
			wait_flag = 1;
			goto point_wait_flag;
		} else {
			req_id = i;
		}
	}
	if (req_id >= 0) {
		p_id_node[req_id].b_using = 1;
		p_id_node[req_id].b_used = 1;
		p_id_node[req_id].pos = req_pos;
		*id = req_id;
	} else {
		wait_flag = 1;
		goto point_wait_flag;
	}

point_wait_flag:
	if (*id >= 0) {
		for (i = *id; i < (id_distance + *id); i++) {
			if (p_id_node[i % id_distance].b_used
					&& (p_id_node[i % id_distance].pos == p_id_pos->cur_pos)) {
				p_id_node[i % id_distance].b_used = 0;
				p_id_pos->cur_pos = (p_id_pos->cur_pos + 1) % p_id_pos->pos_distance;
			}
		}
		//printf("index = %d, req_pos = %d, *id = %d, p_id_pos->cur_pos = %d\n", index, req_pos, *id, p_id_pos->cur_pos);
	}

	return wait_flag;
}

int request_id(id_pos_t *p_id_pos, int req_pos, id_sync_t *sync)
{
	int id = -1;

	if ((req_pos < 0) || ((req_pos > p_id_pos->pos_distance - 1) && (req_pos > sync->gopsize - 1))) {
		hwicodec_log(NULL, C_LOG_ERROR, "invalide req_pos = %d\n", req_pos);
		goto err_req_pos_data;
	}

	pthread_mutex_lock(&p_id_pos->mutex);
	while (b_wait_id(p_id_pos, req_pos, &id, sync)) {
		if (pthread_cond_wait(&p_id_pos->cond, &p_id_pos->mutex) < 0) {
			hwicodec_log(NULL, C_LOG_ERROR, "pthread_cond_wait failed\n");
			goto err_pthread_cond_wait;
		}
	}
	pthread_mutex_unlock(&p_id_pos->mutex);

	return id;

err_pthread_cond_wait:
	pthread_mutex_unlock(&p_id_pos->mutex);
err_req_pos_data:
	return -1;
}

void release_id(id_pos_t *p_id_pos, int id)
{
	if(p_id_pos == NULL) {
		hwicodec_log(NULL, C_LOG_ERROR, "p_id_node is NULL\n");
		return;
	}

	id_node_t *p_id_node = p_id_pos->id_elem.p_id_node;

	pthread_mutex_lock(&p_id_pos->mutex);
	p_id_node[id].b_using = 0;
	pthread_mutex_unlock(&p_id_pos->mutex);
	pthread_cond_broadcast(&p_id_pos->cond);
}

int init_id_pos(id_pos_t *p_id_pos, int pos_distance, int min_id, int max_id, int b_seq)
{
	p_id_pos->cur_pos = 0;
	p_id_pos->pos_distance = pos_distance;
	p_id_pos->id_elem.id_prop.min_id = min_id;
	p_id_pos->id_elem.id_prop.max_id = max_id;
	p_id_pos->id_elem.id_prop.b_seq = b_seq;
	if ((p_id_pos->id_elem.p_id_node = malloc(sizeof(id_node_t) * (max_id - min_id + 1))) == NULL) {
		hwicodec_log(NULL, C_LOG_ERROR, "malloc p_id_node failed\n");
		goto err_malloc_p_id_node;
	}
	memset(p_id_pos->id_elem.p_id_node, 0, sizeof(id_node_t) * (max_id - min_id + 1));

	if (pthread_mutex_init(&p_id_pos->mutex, NULL) < 0) {
		hwicodec_log(NULL, C_LOG_ERROR, "pthread_mutex_init failed\n");
		goto err_pthread_mutex_init;
	}

	if (pthread_cond_init(&p_id_pos->cond, NULL) < 0) {
		hwicodec_log(NULL, C_LOG_ERROR, "pthread_cond_init failed\n");
		goto err_pthread_cond_init;
	}

	return 0;

err_pthread_cond_init:
	pthread_mutex_destroy(&p_id_pos->mutex);
err_pthread_mutex_init:
	free(p_id_pos->id_elem.p_id_node);
	p_id_pos->id_elem.p_id_node = NULL;
err_malloc_p_id_node:
	return -1;
}

void deinit_id_pos(id_pos_t *p_id_pos)
{
	pthread_cond_destroy(&p_id_pos->cond);
	pthread_mutex_destroy(&p_id_pos->mutex);
	free(p_id_pos->id_elem.p_id_node);
	memset(p_id_pos, 0, sizeof(id_pos_t));
}
